/*
 * Description: process monitor dll injector
 */
#include "perf_event_dispatcher.h"
#include <algorithm>
#include <chrono>
#include <thread>
#include "common/session_utils.h"
#include "log.h"
#include "utils/perf_utils.h"
#include "preload/preload_app_path_manager.h"
#include "performance/perf_constants.h"
#include "performance/autorun/autorun_manager.h"
#include "performance/config/perf_config_factory.h"
#include "performance/utils/registry_utils.h"

namespace PERF {

constexpr const int USER_LOGON_CHECK_MAX_TIMES = 100;
constexpr const int USER_LOGON_CHECK_INTERVAL = 5;

PerfEventDispatcher::PerfEventDispatcher() {}

PerfEventDispatcher::~PerfEventDispatcher() {}

void PerfEventDispatcher::OnProcessStart(const std::string &name, const std::string &path, const DWORD processId)
{
    std::unique_lock<std::mutex> lock(preloadMutex);
    std::string pnameLower = name;
    std::transform(pnameLower.begin(), pnameLower.end(), pnameLower.begin(), ::tolower);
    if (PROCESS_PRELOAD_WHITELIST.find(pnameLower) == PROCESS_PRELOAD_WHITELIST.end()) {
        return;
    }
    if (pnameToPids.find(pnameLower) == pnameToPids.end()) {
        pnameToPids.emplace(pnameLower, std::unordered_set<DWORD>());
    }
    pnameToPids[pnameLower].emplace(processId);
    pidToPname.emplace(processId, pnameLower);
}

void PerfEventDispatcher::OnProcessEnd(const DWORD processId)
{
    std::unique_lock<std::mutex> lock(preloadMutex);
    if (pidToPname.find(processId) == pidToPname.end()) {
        return;
    }
    std::string pname = pidToPname[processId];
    pidToPname.erase(processId);
    pnameToPids[pname].erase(processId);
    if (pnameToPids[pname].size() == 0) {
        pnameToPids.erase(pname);
        PROCESS_PRELOAD_ENTRANCE.DoPreload(pname);
    }
}

void PerfEventDispatcher::OnUserLogon()
{
    auto config = PERF_CONFIG_FACTORY.GetConfig();
    if (config != nullptr) {
        for (auto entry : config->moduleNameToAutoruns) {
            AutorunManager::RemoveAutorun(entry.second);
        }
    }
    for (std::string appName : PRELOAD_OFFICE_APPS) {
        PROCESS_PRELOAD_ENTRANCE.DoPreload(appName);
    }
}

void PerfEventDispatcher::CheckUserLogon()
{
    std::thread tCheckUserLogon([&]() {
        int times = USER_LOGON_CHECK_MAX_TIMES;
        bool isUserLogon = false;
        while (times--) {
            if (PERF_UTILS.IsProcessExists(PNAME_EXPLORER)) {
                isUserLogon = true;
                break;
            }
            std::this_thread::sleep_for(std::chrono::seconds(USER_LOGON_CHECK_INTERVAL));
        }
        if (isUserLogon) {
            OnUserLogon();
        }
    });
    tCheckUserLogon.detach();
}

}  // namespace PERF